package org.apache.servicecomb.samples.practise.houserush.sale.service;

import com.alibaba.fastjson.JSON;
import org.apache.http.HttpStatus;
import org.apache.servicecomb.provider.pojo.RpcReference;
import org.apache.servicecomb.samples.practise.houserush.sale.aggregate.Favorite;
import org.apache.servicecomb.samples.practise.houserush.sale.aggregate.HouseOrder;
import org.apache.servicecomb.samples.practise.houserush.sale.aggregate.Sale;
import org.apache.servicecomb.samples.practise.houserush.sale.aggregate.SaleQualification;
import org.apache.servicecomb.samples.practise.houserush.sale.dao.FavoriteDao;
import org.apache.servicecomb.samples.practise.houserush.sale.dao.HouseOrderDao;
import org.apache.servicecomb.samples.practise.houserush.sale.dao.SaleDao;
import org.apache.servicecomb.samples.practise.houserush.sale.dao.SaleQualificationDao;
import org.apache.servicecomb.samples.practise.houserush.sale.redis.RedisKey;
import org.apache.servicecomb.samples.practise.houserush.sale.redis.RedisUtil;
import org.apache.servicecomb.samples.practise.houserush.sale.rpc.RealestateApi;
import org.apache.servicecomb.samples.practise.houserush.sale.rpc.po.House;
import org.apache.servicecomb.samples.practise.houserush.sale.rpc.po.Realestate;
import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.apache.servicecomb.tracing.Span;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toMap;

/**
 * @Author: Snake Man
 * @system:
 * @DateTime: 2022/1/18 13:18
 * @Description:
 */
@Service
public class HouseOrderServiceImpl implements HouseOrderService {

    @Autowired
    private HouseOrderDao houseOrderDao;

    @Autowired
    private FavoriteDao favoriteDao;

    @Autowired
    private SaleQualificationDao saleQualificationDao;

    @Autowired
    private SaleDao saleDao;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedisKey redisKey;

    //调用楼盘管理微服务接口
    @RpcReference(microserviceName = "realestate", schemaId = "realestateApiRest")
    private RealestateApi realestateApi;

    //查询所有活动
    public List<Sale> indexSales() {
        return saleDao.findAll();
    }

    //查询所有活动
    public Sale findSale(int saleId) {
        //查询活动
        Sale sale = saleDao.findOne(saleId);
        //根据活动的楼盘id查询楼盘名称
        Realestate realestate = realestateApi.findRealestate(sale.getRealestateId());
        //设置到活动中
        sale.setRealestateName(realestate.getName());
        return sale;
    }

    //根据楼盘查询活动
    public List<Sale> findSaleByRealestateId(int realestateId) {
        return saleDao.findByRealestateId(realestateId);
    }

    //创建活动
    @Transactional
    public Sale createSale(Sale sale) {
        //在活动中获取房源订单
        List<HouseOrder> houseOrders = sale.getHouseOrders();
        //保存活动数据
        sale.setHouseOrders(null);
        Sale save = saleDao.save(sale);
        //获取活动中的房源id
        List<Integer> houseId = new ArrayList<>();

        houseOrders.forEach(houseOrder -> {
            Sale s = new Sale();
            s.setId(save.getId());
            //绑定房源订单和活动的关系
            houseOrder.setSale(s);
            //获取活动中的房源id，设置到容器中
            houseId.add(houseOrder.getHouseId());
        });
        //对房源进行锁定
        realestateApi.lockHousesForSale(houseId);
        //保存活动房源订单（预下房源订单）
        List<HouseOrder> houseOrderList = houseOrderDao.save(houseOrders);
        save.setHouseOrders(houseOrderList);
        return sale;
    }

    //活动修改
    public Sale updateSale(Sale sale) {
        if (saleDao.exists(sale.getId())) {
            return saleDao.save(sale);
        } else {
            throw new DataRetrievalFailureException("活动不存在，无法修改");
        }
    }

    //删除活动
    @Transactional
    public void removeSale(int saleId) {
        //获取当前活动
        Sale sale = saleDao.findOne(saleId);
        //获取活动中的房源
        List<Integer> houseId = new ArrayList<>();
        List<HouseOrder> houseOrders = sale.getHouseOrders();

        houseOrders.forEach(houseOrder -> {
            houseId.add(houseOrder.getHouseId());
        });

        //解锁房源
        if (!houseId.isEmpty()) {
            realestateApi.updateReleaseLockingStattesForHouses(houseId);
        }
        //删除房源
        saleDao.delete(saleId);
    }

    //活动查询
    @Span
    public Sale findOrderSale(int saleId) {
        //声明活动类
        Sale sale;
        //获取redis中的key
        String saleKey = redisKey.getSaleKey(saleId);//活动
        String saleHashKey = redisKey.getSaleHashKey(saleId);//房源订单
        //从redis获取数据
        String saleStr = redisUtil.get(saleKey);

        //判断redis中是否存在
        if (saleStr == null) {
            //不存在从数据库中获取
            sale = saleDao.findOne(saleId);

            if (sale == null) {
                //不存在，返回null
                return null;
            }

            //清理HouseOrder带的活动sale，这里已经有sale了，节省redis空间清理。
            sale.getHouseOrders().forEach(houseOrder -> {
                houseOrder.setSale(null);
            });
            //保存到redis中，设置过期时间
            redisUtil.set(saleKey, JSON.toJSONString(sale), 600);

            //获取活动中的房源数据
            List<HouseOrder> list = sale.getHouseOrders();

            //清理活动中的房源数据
            sale.setHouseOrders(null);
            //保存到redis中，设置过期时间
            redisUtil.set(redisKey.getSaleNoHouseOrder(saleId), JSON.toJSONString(sale), 600);

            //保存Redis成功后，把HoseOrder设置回来
            sale.setHouseOrders(list);
        } else {
            //把redis查询的数据转为对象
            sale = JSON.parseObject(saleStr, Sale.class);
        }

        //为了保证高性能，返回的活动数据只有基础数据
        //活动中的房源订单，只保留订单状态
        //获取hash数据
        Map<Object, Object> map = redisUtil.hmget(saleHashKey);
        if (map == null || map.size() == 0) {
            //遍历活动中的房源订单
            //for(HouseOrder houseOrder : sale.getHouseOrders()){
            //    //获取房源订单状态
            //    //为了前台客户进行抢房的时候，能够快速判断该房是否可以抢购
            //    map.put(houseOrder.getId().toString(),houseOrder.getState());
            //}

            //归集带map
            map = sale.getHouseOrders().stream()
                    .collect(toMap(h -> h.getId().toString(), HouseOrder::getState));

            //保存房源订单到redis中
            redisUtil.hmset(saleHashKey, map, 600);

        }
        //处理返回数据
        for (HouseOrder h : sale.getHouseOrders()) {
            h.setState(map.get(h.getId() + "").toString());
            h.setSale(null);
            h.setCustomerId(null);
            h.setOrderedAt(null);
            h.setCreatedAt(null);
            h.setFavorites(null);
            h.setUpdatedAt(null);
            h.setHouseId(null);
        }
        return sale;
    }

    //同步修改活动资格
    public void updateSaleQualification(List<SaleQualification> saleQualifications) {
        if (null != saleQualifications) {
            //遍历数据的用户id和活动id
            saleQualifications.forEach(saleQualification -> {
                //根据活动id和客户id查询
                SaleQualification qualification = saleQualificationDao.findBySaleIdAndCustomerId(
                        saleQualification.getSaleId(), saleQualification.getCustomerId());

                if (qualification == null) {
                    //数据不存在则新增，设置下单为0
                    saleQualification.setOrderCount(0);
                    saleQualificationDao.save(saleQualification);
                } else {
                    //数据存在则修改
                    qualification.setQualificationCount(saleQualification.getQualificationCount());
                    saleQualificationDao.save(qualification);
                }
            });
        }
    }

    //同步删除活动资格
    @Transactional
    public void removeSaleQualification(int customerId) {
        saleQualificationDao.deleteAllByCustomerId(customerId);
    }

    //查询活动房源数量
    public int countHouseBySaleId(int saleId) {
        return houseOrderDao.countBySaleId(saleId);
    }

    //根据客户id查询收藏
    public List<Favorite> findMyFavorite(int customerId) {
        return favoriteDao.findAllByCustomerId(customerId);
    }

    //客户收藏房源
    @Transactional
    public Favorite addFavorite(int customerId, int houseOrderId) {
        //查询房源
        HouseOrder houseOrder = houseOrderDao.findOne(houseOrderId);

        if (houseOrder == null) {
            //房源不存在则炮异常
            throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "房源订单不存在");
        }

        //计算该客户id和房源id的记录数，存在则是已收藏
        if (favoriteDao.countByCustomerIdAndHouseOrderId(customerId, houseOrderId) == 0) {
            //没有则添加收藏
            Favorite favorite = new Favorite();

            favorite.setCustomerId(customerId);
            favorite.setHouseOrder(houseOrder);

            return favoriteDao.save(favorite);
        } else {
            throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "房源已被标记为收藏");
        }
    }

    //根据id查询收藏
    public Favorite findFavorite(int id) {
        return favoriteDao.findOne(id);
    }

    //删除收藏
    public void removeFavorite(int id) {
        favoriteDao.delete(id);
    }

    //根据房源订单id查询房源订单
    public HouseOrder findOne(int houseOrderId) {
        return houseOrderDao.findOne(houseOrderId);
    }

    //根据房源id查询房源订单
    public HouseOrder findAllByHouseId(Integer houseId) {
        return houseOrderDao.findAllByHouseId(houseId);
    }

    //根据客户id查询该客户的所有房源订单
    public List<HouseOrder> findAllByCustomerId(int cucustomerId) {
        return houseOrderDao.findAllByCustomerId(cucustomerId);
    }

    //根据客户id查询活动资格表
    public List<SaleQualification> findByCustomerId(int customerId) {
        return saleQualificationDao.findByCustomerId(customerId);
    }

    //根据客户id和活动id查询活动资格表
    public SaleQualification findByCustomerIdAndSaleId(int customerId, int saleId) {
        return saleQualificationDao.findBySaleIdAndCustomerId(saleId, customerId);
    }

    //抢购房源
    @Transactional
    public HouseOrder placeHouseOrder(int customerId, int saleId, int houseOrderId, int orderId) {
        //获取房源订单状态的key
        String saleHashKey = redisKey.getSaleHashKey(saleId);
        //获取该房源订单的状态
        Object hstate = redisUtil.hget(saleHashKey, houseOrderId + "");
        //如果是已认购，则抛异常通知
        if ("confirmed".equals(hstate)) {
            throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "房源已被抢购，请稍后再试");
        }
        //判断活动是否已经开始
        if (isSaleOpen(saleId)) {
            SaleQualification qualification = saleQualificationDao.findBySaleIdAndCustomerId(saleId, customerId);
            if (!qualification.hasPlaceQualification()) {
                throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "",
                        "没有足够的房源，房源数是" + qualification.getQualificationCount() +
                                "，已订购总数是" + qualification.getOrderCount());
            }
            //创建房源订单
            HouseOrder houseOrder = new HouseOrder();
            houseOrder.setId(houseOrderId);
            houseOrder.setState("confirmed");
            houseOrder.setOrderedAt(new Date());
            houseOrder.setCustomerId(customerId);
            //修改房源订单
            int count = houseOrderDao.updateByIdAndCustomerIdIsNull(customerId, houseOrder.getState(), houseOrder.getOrderedAt(), houseOrderId);
            //修改成功数量小于1，表示修改失败，抢购失败
            if (count < 1) {
                redisUtil.hset(saleHashKey, houseOrderId + "", "confirmed", 600);
                throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "房源已被抢购，请稍后再试");
            }
            //抢购成功，下单数加一
            qualification.addOrderCount();
            saleQualificationDao.saveAndFlush(qualification);
            redisUtil.hset(saleHashKey, houseOrderId + "", "confirmed", 600);
            return houseOrder;
        } else {
            throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "该房源现在不出售");
        }
    }

    //判断活动是否已经开始
    private boolean isSaleOpen(int saleId) {
        //获取没有房源订单的活动数据
        String obj = redisUtil.get(redisKey.getSaleNoHouseOrder(saleId));
        //判断redis查询的值是否为空
        if (obj == null) {
            //根据活动id查询活动数据
            Sale sale = saleDao.findOne(saleId);
            //如果为空表示活动不存在，返回false
            if (sale == null) {
                return false;
            }
            //如果不为空，移除活动的房源订单数据
            sale.setHouseOrders(null);
            //保存活动数据到redis中
            obj = JSON.toJSONString(sale);
            redisUtil.set(redisKey.getSaleNoHouseOrder(saleId), obj, 600);
        }
        //解析redis的活动数据
        Sale sale = JSON.parseObject(obj, Sale.class);
        //如果状态为opening，则表示活动开始
        return "opening".equals(sale.getState());
    }

    //取消房源订单
    @Transactional
    public HouseOrder cancelHouseOrder(int customerId, int houseOrderId, int orderId){
        //根据房源订单id查询
        HouseOrder houseOrder = houseOrderDao.findOneForUpdate(houseOrderId);
        Sale sale = houseOrder.getSale();
       //判断活动是否开售中
        if (null != sale && "opening".equals(sale.getState())) {
          //判断当前客户是否是房源订单客户
            if (customerId == houseOrder.getCustomerId()) {
                  //取消订单
                houseOrder.setCustomerId(null);
                houseOrder.setState("new");
                houseOrder.setOrderedAt(null);
                houseOrderDao.save(houseOrder);
                //清理redis
                redisUtil.hdel(redisKey.getSaleHashKey(houseOrder.getSale().getId()), houseOrderId + "");
                //查询活动资格
                SaleQualification qualification = saleQualificationDao.findBySaleIdAndCustomerId(sale.getId(), customerId);
                //下单总数减一
                qualification.minusOrderCount();
                saleQualificationDao.saveAndFlush(qualification);
                return houseOrder;
            } else {
                throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "客户不能取消未认购的房源订单");
            }
        } else {
            throw new InvocationException(HttpStatus.SC_BAD_REQUEST, "", "该房源未被认购");
        }
    }

}
